package com.yzd.learnjava.concurrency;

import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;

import com.yzd.learnjava.util.Print;

public class ToastOMatic {
	public static void main(String[] args) throws InterruptedException {
		ToastQueue dryQueue = new ToastQueue();
		ToastQueue butteredQueue = new ToastQueue();
		ToastQueue finishedQueue = new ToastQueue();
		ExecutorService exec = Executors.newCachedThreadPool();
		exec.execute(new Toaster(dryQueue));
		exec.execute(new Butterer(dryQueue, butteredQueue));
		exec.execute(new Jammer(butteredQueue, finishedQueue));
		exec.execute(new Eater(finishedQueue));
		
		TimeUnit.SECONDS.sleep(5);
		exec.shutdown();
	}
}

class Toast{
	public enum Status{DRY, BUTTERED, JAMMED}
	private Status status = Status.DRY;
	private final int id ;
	public Toast(int idn) {
		id = idn;
	}
	public void butter() {
		status = Status.BUTTERED;
	}
	public void jam() {
		status = Status.JAMMED;
	}
	public Status getStatus() {
		return status;
	}
	public int getId() {
		return id;
	}
	public String toString() {
		return "Toast " + id + ": " + status;
	}
}

class ToastQueue extends LinkedBlockingQueue<Toast>{}

class Toaster implements Runnable{
	private ToastQueue toastQueue;
	private int count = 0;
	private Random random = new Random(47);
	public Toaster(ToastQueue queue) {
		toastQueue = queue;
	}
	public void run() {
		try {
			while(!Thread.interrupted()) {
				TimeUnit.MILLISECONDS.sleep(100 + random.nextInt(500));
				Toast t = new Toast(count++);
				Print.print(t);
				toastQueue.add(t);
			}
		} catch (InterruptedException e) {
			Print.print("Toaster Interrupted");
			e.printStackTrace();
		}
		Print.print("Toaster Off");
	}
}

class Butterer implements Runnable{
	private ToastQueue dryQueue ;
	private ToastQueue butteredQueue;
	public Butterer(ToastQueue dry, ToastQueue buttered) {
		dryQueue = dry;
		butteredQueue = buttered;
	}
	public void run() {
		try {
			while(!Thread.interrupted()) {
				Toast t = dryQueue.take();
				t.butter();
				Print.print(t);
				butteredQueue.put(t);
			}
		} catch (InterruptedException e) {
			Print.print("Butterer Interrupted");
			e.printStackTrace();
		}
		Print.print("Butterer Off");
	}
}

class Jammer implements Runnable{
	private ToastQueue butteredQueue;
	private ToastQueue finishedQueue;
	public Jammer(ToastQueue buttered, ToastQueue finished) {
		butteredQueue = buttered;
		finishedQueue = finished;
	}
	public void run() {
		try {
			while(!Thread.interrupted()) {
				Toast t = butteredQueue.take();
				t.jam();
				Print.print(t);
				finishedQueue.put(t);
			}
		} catch (InterruptedException e) {
			Print.print("Jammer Interrupted");
			e.printStackTrace();
		}
		Print.print("Jammer Off");
	}
}

class Eater implements Runnable{
	private ToastQueue finishedQueue ;
	private int counter = 0;
	public Eater(ToastQueue finished) {
		finishedQueue = finished;
	}
	public void run() {
		try {
			while(!Thread.interrupted()) {
				Toast t = finishedQueue.take();
				if(t.getId() != counter++ || t.getStatus() != Toast.Status.JAMMED) {
					Print.print(">>>> ERROR: " + t);
					System.exit(1);
				} else {
					Print.print("Chomp! " + t);
				}
			}
		} catch (InterruptedException e) {
			Print.print("Eater Interrupted");
			e.printStackTrace();
		}
		Print.print("Eater Off");
	}
}






















